package jf.collections;

import java.util.*;
import static java.lang.Math.min;

import static jf.collections.Tuple.asT;
import jf.collections.primitive.DirectArrayList;

/**
 * @author ivanalx
 * @date 06.05.2009 15:26:15
 */
public class CollectionF {
	private CollectionF() {
	}

	//direct mapping from array to collection
	public static <A> ListX<A> direct(A... a) {
		return new DirectArrayList<A>(a);
	}

	public static <T> ListX<T> newList() {
		return new ArrayListX<T>();
	}

	public static <T> ListX<T> newList(int capacity) {
		return new ArrayListX<T>(capacity);
	}

	public static <T> ListX<T> newLinkedList() {
		return new LinkedListX<T>();
	}

	public static <T> Set<T> newSet() {
		return new HashSet<T>();
	}

	public static <T> Set<T> newLinkedSet() {
		return new LinkedHashSet<T>();
	}

	public static <K, T> Map<K, T> newMap() {
		return new HashMap<K, T>();
	}

	public static <K, T> Map<K, T> newMap(int capacity) {
		return new HashMap<K, T>(capacity);
	}

	public static <K, T> Map<K, T> newMap(int capacity, float loadFactor) {
		return new HashMap<K, T>(capacity, loadFactor);
	}

	//simple from collection creation

	public static <T> ListX<T> asList(T... forList) {
		return new ArrayListX<T>(Arrays.asList(forList));
	}

	public static <T> ListX<T> asList(Collection<? extends T>... forList) {
		int size = 0;
		for (Collection<? extends T> ts : forList) {
			size += ts.size();
		}
		ListX<T> list = new ArrayListX<T>(size);
		for (Collection<? extends T> ts : forList) {
			list.add2This(ts);
		}
		return list;
	}

	public static <T> Set<T> asSet(T... forSet) {
		return new HashSet<T>(Arrays.asList(forSet));
	}

	public static <T> Set<T> asSet(Collection<T> forSet) {
		return new HashSet<T>(forSet);
	}

	public static <K,T> Map<K, T> asMap(Iterable<K> a, Iterable<T> b) {
		Iterator<K> iA = a.iterator();
		Iterator<T> iB = b.iterator();
		ListX<Tuple2<K, T>> list = new LinkedListX<Tuple2<K,T>>();
		while (iA.hasNext() && iB.hasNext()) {
			list.add(asT(iA.next(), iB.next()));
		}
		return asMap(list);
	}

	public static <K, T> Map<K, T> asMap(Tuple2<K, T>... entries) {
		if (entries == null) throw new IllegalArgumentException("Can't create map from null");
		Map<K, T> map = new HashMap<K, T>(entries.length);
		for (Tuple2<K, T> t : entries) {
			map.put(t._1, t._2);
		}
		return map;
	}

	public static <K, T> Map<K, T> asMap(Collection<Tuple2<K, T>> entries) {
		if (entries == null) throw new IllegalArgumentException("Can't create map from null");
		HashMap<K, T> map = new HashMap<K, T>(entries.size());
		for (Tuple2<K, T> t : entries) {
			map.put(t._1, t._2);
		}
		return map;
	}

	@SuppressWarnings({"unchecked"})
	public static <K, T> Map<K, T> asMap(Object... entries) {
		if (entries == null) throw new IllegalArgumentException("Can't create map from null");
		if (entries.length % 2 != 0) {
			throw new IllegalArgumentException("Number of parameters must be odd, but was " + entries.length);
		}

		int length = entries.length / 2;
		Map<K, T> map = new HashMap<K, T>(length);
		for (int i = 0; i < length; i++) {
			map.put((K) entries[i * 2], (T) entries[i * 2 + 1]);
		}
		return map;
	}

	//immutable from varargs creation

	public static <T> Set<T> asISet(T... forSet) {
		return Collections.unmodifiableSet(asSet(forSet));
	}

	public static <T> List<T> asIList(T... forList) {
		return Collections.unmodifiableList(asList(forList));
	}

	public static <K, T> Map<K, T> asIMap(Tuple2<K, T>... entries) {
		return Collections.unmodifiableMap(asMap(entries));
	}


	//immutable from Collection creation

	public static <T> Set<T> asISet(Collection<T> forSet) {
		return Collections.unmodifiableSet(asSet(forSet));
	}

	public static <T> List<T> asIList(Collection<T> forList) {
		return Collections.unmodifiableList(asList(forList));
	}

	public static <T> Collection<T> asICol(Collection<T> forCol) {
		return Collections.unmodifiableCollection(forCol);
	}

	public static <K, T> Map<K, T> asIMap(Collection<Tuple2<K, T>> entries) {
		return Collections.unmodifiableMap(asMap(entries));
	}


	public static <T> Set<T> immutable(Set<T> set) {
		return Collections.unmodifiableSet(set);
	}

	public static <T> List<T> immutable(List<T> list) {
		return Collections.unmodifiableList(list);
	}

	public static <T> Collection<T> immutable(Collection<T> list) {
		return Collections.unmodifiableCollection(list);
	}

	public static <K, T> Map<K, T> immutable(Map<K, T> map) {
		return Collections.unmodifiableMap(map);
	}

	// empty
	public static <T> List<T> emptyList() {
		return new ArrayList<T>();
	}

	public static <T> List<T> emptyImmutableList() {
		return Collections.emptyList();
	}

	public static <T> Set<T> emptySet() {
		return new HashSet<T>();
	}

	public static <T> Set<T> emptyImmutableSet() {
		return Collections.emptySet();
	}

	public static <K, V> Map<K, V> emptyMap() {
		return new HashMap<K, V>();
	}

	public static <K, V> Map<K, V> emptyImmutableMap() {
		return new HashMap<K, V>();
	}


	@SuppressWarnings("unchecked")
	public static <T> T[] array(Collection<T> list) {
		return (T[]) list.toArray();
	}

	public static byte[] byteArray(byte... c) {
		return c;
	}

	public static char[] charArray(char... value) {
		return value;
	}

	public static int[] intArray(int... c) {
		return c;
	}

	public static long[] longArray(long... value) {
		return value;
	}

	public static float[] floatArray(float... c) {
		return c;
	}

	public static double[] doubleArray(double... c) {
		return c;
	}

	public static <T> T[] array(T... c) {
		return c;
	}


	public static ListX<Character> asList(char[] chars) {
		ListX<Character> list = new ArrayListX<Character>(chars.length);
		for (char c : chars) {
			list.add(c);
		}
		return list;
	}

	public static ListX<Integer> asList(int[] ints) {
		ListX<Integer> list = new ArrayListX<Integer>(ints.length);
		for (int i : ints) {
			list.add(i);
		}
		return list;
	}

	public static ListX<Integer> asList(int[] ints, int offset, int length) {
		ListX<Integer> list = new ArrayListX<Integer>(ints.length);
		for (int i = offset, intsLength = min(offset + length, ints.length); i < intsLength; i++) {
			list.add(ints[i]);
		}
		return list;
	}

	public static ListX<Double> asList(double[] doubles) {
		ListX<Double> list = new ArrayListX<Double>(doubles.length);
		for (double v : doubles) {
			list.add(v);
		}
		return list;
	}

	public static ListX<Double> asList(double[] doubles, int offset, int length) {
		ListX<Double> list = new ArrayListX<Double>(doubles.length);
		for (int i = offset, intsLength = min(offset + length, doubles.length); i < intsLength; i++) {
			list.add(doubles[i]);
		}
		return list;
	}


	public static ListX<Float> asList(float[] floats) {
		ListX<Float> list = new ArrayListX<Float>(floats.length);
		for (float v : floats) {
			list.add(v);
		}
		return list;
	}

	public static ListX<Float> asList(float[] floats, int offset, int length) {
		ListX<Float> list = new ArrayListX<Float>(floats.length);
		for (int i = offset, intsLength = min(offset + length, floats.length); i < intsLength; i++) {
			list.add(floats[i]);
		}
		return list;
	}


	public static ListX<Long> asList(long[] longs) {
		ListX<Long> list = new ArrayListX<Long>(longs.length);
		for (long v : longs) {
			list.add(v);
		}
		return list;
	}

	public static ListX<Long> asList(long[] longs, int offset, int length) {
		ListX<Long> list = new ArrayListX<Long>(longs.length);
		for (int i = offset, intsLength = min(offset + length, longs.length); i < intsLength; i++) {
			list.add(longs[i]);
		}
		return list;
	}


	public static ListX<Boolean> asList(boolean[] booleans) {
		ListX<Boolean> list = new ArrayListX<Boolean>(booleans.length);
		for (boolean v : booleans) {
			list.add(v);
		}
		return list;
	}

	public static ListX<Boolean> asList(boolean[] booleans, int offset, int length) {
		ListX<Boolean> list = new ArrayListX<Boolean>(booleans.length);
		for (int i = offset, intsLength = min(offset + length, booleans.length); i < intsLength; i++) {
			list.add(booleans[i]);
		}
		return list;
	}



	public static int[] primive(Collection<Integer> list) {
		int[] array = new int[list.size()];
		Integer[] ints = list.toArray(new Integer[list.size()]);
		int i = 0;
		for (Integer p : ints) {
			array[i++] = p;
		}
		return array;
	}

	public static byte[] primive(Collection<Byte> list) {
		byte[] array = new byte[list.size()];
		Byte[] ints = list.toArray(new Byte[list.size()]);
		int i = 0;
		for (Byte p : ints) {
			array[i++] = p;
		}
		return array;
	}

	public static double[] primive(Collection<Double> list) {
		double[] array = new double[list.size()];
		Double[] ints = list.toArray(new Double[list.size()]);
		int i = 0;
		for (Double p : ints) {
			array[i++] = p;
		}
		return array;
	}

	public static float[] primive(Collection<Float> list) {
		float[] array = new float[list.size()];
		Float[] ints = list.toArray(new Float[list.size()]);
		int i = 0;
		for (Float p : ints) {
			array[i++] = p;
		}
		return array;
	}
}
